﻿extern alias core;

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using core::OmniKits.Security.Cryptography;
using System.IO;
using System.Text;

namespace OmniKitsCoreTest
{
    [TestClass]
    public class CRC32Test
    {
        [TestMethod]
        public void CrcVerify()
        {
            var bytes = Encoding.ASCII.GetBytes("123456789");

            using (var crc = new CRC32())
            {
                using (var ms = new MemoryStream(bytes, false))
                    Assert.AreEqual("CB-F4-39-26", BitConverter.ToString(crc.ComputeHash(ms)));
                using (var rs = typeof(UltraLargeTestData).Assembly.GetManifestResourceStream("OmniKitsCoreTest.ZeroToD7FF.txt"))
                    Assert.AreEqual("5A-18-16-9F", BitConverter.ToString(crc.ComputeHash(rs)));
                using (var rs = typeof(UltraLargeTestData).Assembly.GetManifestResourceStream("OmniKitsCoreTest.E000ToFFFF.txt"))
                    Assert.AreEqual("AD-F7-95-A2", BitConverter.ToString(crc.ComputeHash(rs)));
            }

            // CRC-32 with reversed polynomial
            var defaultParams = new CRC32Parameters(CRC32.DefaultPolynomial);
            using (var crc = new CRC32(defaultParams))
            {
                using (var ms = new MemoryStream(bytes, false))
                    Assert.AreEqual("CB-F4-39-26", BitConverter.ToString(crc.ComputeHash(ms)));
            }

            // CRC-32 without reversed polynomial
            var normalParams = new CRC32Parameters(0x04C11DB7, flags: CrcPolynomialFlags.None);
            using (var crc = new CRC32(normalParams))
            {
                using (var ms = new MemoryStream(bytes, false))
                    Assert.AreEqual("CB-F4-39-26", BitConverter.ToString(crc.ComputeHash(ms)));
            }
            Assert.AreEqual(defaultParams, CRC32.TranslateParameters(normalParams));

            // CRC-32 with reciprocal translating
            var reciprocalParams = new CRC32Parameters(unchecked((int)0xDB710641), flags: CrcPolynomialFlags.Reciprocal);
            using (var crc = new CRC32(reciprocalParams))
            {
                using (var ms = new MemoryStream(bytes, false))
                    Assert.AreEqual("CB-F4-39-26", BitConverter.ToString(crc.ComputeHash(ms)));
            }
            Assert.AreEqual(defaultParams, CRC32.TranslateParameters(reciprocalParams));

            // CRC-32 with reversed reciprocal translating
            // TODO: reversed codeword check
            var reversedReciprocalParams = new CRC32Parameters(unchecked((int)0x82608EDB), flags: CrcPolynomialFlags.ReversedReciprocal);
            using (var crc = new CRC32(reversedReciprocalParams))
            {
                using (var ms = new MemoryStream(bytes, false))
                    Assert.AreEqual("CB-F4-39-26", BitConverter.ToString(crc.ComputeHash(ms)));
            }
            Assert.AreEqual(defaultParams, CRC32.TranslateParameters(reversedReciprocalParams));

            // Jam-CRC
            using (var crc = new CRC32(CRC32.DefaultPolynomial, -1, 0))
            {
                using (var ms = new MemoryStream(bytes, false))
                    Assert.AreEqual("34-0B-C6-D9", BitConverter.ToString(crc.ComputeHash(ms)));
            }

            // CRC-32C
            using (var crc = new CRC32(CRC32.CastagnoliPolynomial))
            {
                using (var ms = new MemoryStream(bytes, false))
                    Assert.AreEqual("E3-06-92-83", BitConverter.ToString(crc.ComputeHash(ms)));
            }

            // CRC-32D
            using (var crc = new CRC32(CRC32.GetReversedPolynomial(unchecked((int)0xA833982B))))
            {
                using (var ms = new MemoryStream(bytes, false))
                    Assert.AreEqual("87-31-55-76", BitConverter.ToString(crc.ComputeHash(ms)));
            }
        }

        [TestMethod]
        public void CheckPolynomial()
        {
            for (var i = 0u; i < 0xFFFFFFFF; i++)
            {
                var v = (int)i;
                Assert.IsTrue(CRC32.IsPolynomialValid(v | 1));
                Assert.IsTrue(CRC32.IsReversedPolynomialValid(v | -1));
                Assert.AreEqual(v, CRC32.FromReversedReciprocalPolynomial(CRC32.GetReversedReciprocalPolynomial(v)));
            }
        }
    }
}
